﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using UnityEngine;
using NinjaToolbox.SubComponentModule.Internal;
using System.Xml;
using System.Xml.Schema;
using System.Reflection;

namespace NinjaToolbox.SubComponentModule
{
    [Serializable]
    public class SubComponentContainer
    {
        static Type[] sCacheSubComponentModelTypeArray;

        Action<IEnumerator> mStartCoroutineFunc;
        List<SubComponentInfo> mSubComponentList = new List<SubComponentInfo>();
        List<SubComponentModelBase> mSubComponentModelList = new List<SubComponentModelBase>();


        public void AddSubComponent(SubComponentBase subComponent, Action<IEnumerator> startCoroutineFunc)
        {
            var newSubComponentInfo = new SubComponentInfo() { SubComponent = subComponent, SubComponentCachedInterface = subComponent as ISubComponent };

            newSubComponentInfo = CheckComponent(newSubComponentInfo);

            mSubComponentList.Add(newSubComponentInfo);
        }

        public bool RemoveSubComponent(SubComponentBase subComponent)
        {
            var subComponentInfo = new SubComponentInfo() { SubComponent = subComponent, SubComponentCachedInterface = subComponent as ISubComponent };

            subComponentInfo.SubComponentCachedInterface.OnDisable();
            subComponentInfo.SubComponentCachedInterface.OnDestroy();

            return mSubComponentList.Remove(subComponentInfo);
        }

        public T GetSubComponent<T>()
            where T : class
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var concertComponent = mSubComponentList[i].SubComponent as T;
                if (concertComponent != null)
                    return concertComponent;
            }

            return null;
        }

        public T[] GetSubComponents<T>()
            where T : class
        {
            var resultList = new List<T>();

            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var concertComponent = mSubComponentList[i].SubComponent as T;
                if (concertComponent != null)
                    resultList.Add(concertComponent);
            }

            return resultList.ToArray();
        }

        public object[] GetSubComponents(Type type)
        {
            var resultList = new List<object>();

            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var item = mSubComponentList[i];

                if (item.SubComponent.GetType() == type)
                    resultList.Add(item);
            }

            return resultList.ToArray();
        }

        public void Initialization(SubComponentContext context, Action<IEnumerator> startCoroutineFunc)
        {
            mStartCoroutineFunc = startCoroutineFunc;

            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var item = mSubComponentList[i];

                item.SubComponentCachedInterface.ContextInitialization(context);
                item.ChangeToStaticSubComponent();

                mSubComponentList[i] = item;
            }
        }

        public void DoAwake()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
                mSubComponentList[i].SubComponentCachedInterface.Awake();
        }

        public void DoOnEnable()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var item = mSubComponentList[i];

                if (item.SubComponent.GetModel().Enabled)
                    item.SubComponentCachedInterface.OnEnable();
            }
        }

        public void DoStart()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
                mSubComponentList[i].SubComponentCachedInterface.Start();
        }

        public void DoOnDisable()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                if (mSubComponentList[i].SubComponent.GetModel().Enabled)
                    mSubComponentList[i].SubComponentCachedInterface.OnDisable();
            }
        }

        public void DoOnDestroy()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
                mSubComponentList[i].SubComponentCachedInterface.OnDestroy();
        }

        public void DoUpdate()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var item = mSubComponentList[i];

                mSubComponentList[i] = CheckComponent(item);

                if (item.SubComponent.GetModel().Enabled)
                    item.SubComponentCachedInterface.Update();
            }
        }

        public void DoFixedUpdate()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var item = mSubComponentList[i];

                if (item.SubComponent.GetModel().Enabled)
                    item.SubComponentCachedInterface.FixedUpdate();
            }
        }

        public void DoLateUpdate()
        {
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                var item = mSubComponentList[i];

                mSubComponentList[i] = CheckComponent(item);

                if (item.SubComponent.GetModel().Enabled)
                    item.SubComponentCachedInterface.LateUpdate();
            }
        }

        public ComponentSerializeUnion[] SerializeToUnion()
        {
            var result = new List<ComponentSerializeUnion>();
            var queue = new Queue<Union>();

            for (int i = 0, iMax = mSubComponentModelList.Count; i < iMax; i++)
            {
                var model = mSubComponentModelList[i];

                queue.Clear();
                var unionSerializeWriter = new UnionSerializeWriter(queue);

                unionSerializeWriter.Write(model.GetType().Assembly.GetName().Name);
                unionSerializeWriter.Write(model.GetType().FullName);

                (model as IUnionSerializable).Serialize(unionSerializeWriter);

                result.Add(new ComponentSerializeUnion() { unionArray = queue.ToArray() });
            }

            return result.ToArray();
        }

        public void DeserializeFromUnion(ComponentSerializeUnion[] serializeData)
        {
            mSubComponentList.Clear();
            mSubComponentModelList.Clear();

            for (int i = 0, iMax = serializeData.Length; i < iMax; i++)
            {
                var queue = new Queue<Union>(serializeData[i].unionArray);
                var unionSerializeReader = new UnionSerializeReader(queue);

                var assemblyName = unionSerializeReader.ReadString();
                var typeFullName = unionSerializeReader.ReadString();
                var assembly = Assembly.Load(assemblyName);
                var type = assembly.GetType(typeFullName);

                var subComponentModel = Activator.CreateInstance(type) as SubComponentModelBase;
                mSubComponentModelList.Add(subComponentModel);

                (subComponentModel as IUnionSerializable).Deserialize(unionSerializeReader);

                var subComponent = SubComponentFactory.CreateSubComponent(subComponentModel);
                mSubComponentList.Add(new SubComponentInfo() { SubComponent = subComponent, SubComponentCachedInterface = subComponent });
            }
        }

        IEnumerator InvokeSubComponentStart(ISubComponent subComponent)
        {
            yield return null;

            var flag = false;
            for (int i = 0, iMax = mSubComponentList.Count; i < iMax; i++)
            {
                if (mSubComponentList[i].SubComponentCachedInterface == subComponent)
                {
                    flag = true;
                    break;
                }
            }

            if (flag)
                subComponent.Start();
        }

        SubComponentInfo CheckComponent(SubComponentInfo subComponentInfo)
        {
            if (subComponentInfo.SubComponent.GetModel().Enabled)
            {
                if (!subComponentInfo.IsAwaked)
                {
                    subComponentInfo.SubComponentCachedInterface.Awake();
                    subComponentInfo.IsAwaked = true;
                }

                if (!subComponentInfo.LastIsOnEnabled)
                {
                    subComponentInfo.SubComponentCachedInterface.OnEnable();
                }

                if (!subComponentInfo.IsStarted)
                {
                    var enumerator = InvokeSubComponentStart(subComponentInfo.SubComponentCachedInterface);
                    mStartCoroutineFunc(enumerator);
                    subComponentInfo.IsStarted = true;
                }
            }
            else
            {
                if (subComponentInfo.LastIsOnEnabled)
                    subComponentInfo.SubComponentCachedInterface.OnDisable();
            }

            subComponentInfo.LastIsOnEnabled = subComponentInfo.SubComponent.GetModel().Enabled;

            return subComponentInfo;
        }
    }
}
